package etcd

import (
	"context"
	"encoding/json"
	"fmt"
	log "gitee.com/vrv_media/go-micro-framework/pkg/logger"
	"gitee.com/vrv_media/go-micro-framework/registry"
	regOps "gitee.com/vrv_media/go-micro-framework/registry/options"
	clientv3 "go.etcd.io/etcd/client/v3"
	"google.golang.org/grpc"
	"time"
)

type EtcdDiscovery struct {
	namespace string
	client    *clientv3.Client
	kv        clientv3.KV
}

func NewEtcdDiscovery(options *regOps.DiscoveryOptions) *EtcdDiscovery {
	client, err := clientv3.New(clientv3.Config{
		Endpoints:   []string{options.Address},
		DialTimeout: time.Second, DialOptions: []grpc.DialOption{grpc.WithBlock()},
	})
	if err != nil {
		log.ErrorF("create etcd client failed.error=%v", err.Error())
		panic(err)
	}

	return &EtcdDiscovery{
		namespace: "/microservices",
		client:    client,
		kv:        clientv3.NewKV(client),
	}
}

// GetService return the service instances in memory according to the service name.
func (d *EtcdDiscovery) GetService(ctx context.Context, name string) ([]*registry.ServiceInstance, error) {
	key := fmt.Sprintf("%s/%s", d.namespace, name)
	resp, err := d.kv.Get(ctx, key, clientv3.WithPrefix())
	if err != nil {
		return nil, err
	}
	items := make([]*registry.ServiceInstance, 0, len(resp.Kvs))
	for _, kv := range resp.Kvs {
		si, err := unmarshal(kv.Value)
		if err != nil {
			return nil, err
		}
		if si.Name != name {
			continue
		}
		items = append(items, si)
	}
	return items, nil
}

// Watch creates a watcher according to the service name.
func (d *EtcdDiscovery) Watch(ctx context.Context, name string) (registry.Watcher, error) {
	key := fmt.Sprintf("%s/%s", d.namespace, name)
	return newWatcher(ctx, key, name, d.client)
}

func unmarshal(data []byte) (si *registry.ServiceInstance, err error) {
	err = json.Unmarshal(data, &si)
	return
}
